import type {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
import axios from 'axios'
import { ElMessage } from 'element-plus'
import type { ResultData } from './types'
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/config/serviceLoading'
import router from '@/routers'
import { getVariableName } from '@/utils'

export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
  noLoading?: boolean
}

const env = import.meta.env
let baseUrl = env.VITE_GLOB_API_URL
// 生产兼容外部修改
if (env.PROD) {
  const ENV_NAME = getVariableName(env.VITE_APP_TITLE)
  baseUrl = window[ENV_NAME].VITE_GLOB_API_URL
}
const config = {
  // 默认地址请求地址，可在 .env.** 文件中修改或者打包后的appConfig.js文件
  baseURL: baseUrl,
  // 设置超时时间
  timeout: 30000,
  // 跨域时候允许携带凭证
  withCredentials: true,
}
// 接口异常处理
function dealError(config?: CustomAxiosRequestConfig, message?: string, code?: number) {
  const userStore = useUserStore()
  switch (code) {
    case 401:
      ElMessage.error(message || '登录失效,请重新登录')
      setTimeout(async () => {
        await userStore.logout()
        // 跳转到统一门户登录页
        if (env.MODE !== 'development')
          location.href = env.VITE_PIPE_PATH
      }, 1000)
      break
    case 403:
      if (config?.method === 'get')
        router.push('/403')
      else ElMessage.error(message || '服务出错')
      break
    case 500:
      ElMessage.error(message || '服务出错')
      break
    default:
      ElMessage.error(message || '服务出错')
  }
}

class RequestHttp {
  service: AxiosInstance
  public constructor(config: AxiosRequestConfig) {
    // instantiation
    this.service = axios.create(config)

    this.service.interceptors.request.use(
      (config: CustomAxiosRequestConfig) => {
        const userStore = useUserStore()
        // 当前请求不需要显示 loading，在 api 服务中通过指定的第三个参数: { noLoading: true } 来控制
        // eslint-disable-next-line ts/no-unused-expressions
        config.noLoading || showFullScreenLoading()
        if (config.headers && userStore.token) {
          config.headers['Gw-Authorization-Rt'] = userStore.token
          config.headers.Authorization = userStore.token
        }
        return config
      },
      (error: AxiosError) => {
        return Promise.reject(error)
      },
    )

    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data, config } = response
        tryHideFullScreenLoading()
        if (data.status === '200' || data.code === 200) {
          return Promise.resolve(data)
        }
        else {
          dealError(config, data?.message, data?.code || Number(data?.status))
          return Promise.reject(data)
        }
      },
      async (error: AxiosError) => {
        const { config, response, message } = error
        tryHideFullScreenLoading()
        // 请求超时 && 网络错误单独判断，没有 response
        if (message.includes('timeout')) {
          ElMessage.error('请求超时！请您稍后重试')
          return Promise.reject(error)
        }
        if (message.includes('Network Error')) {
          ElMessage.error('网络错误！请您稍后重试')
          return Promise.reject(error)
        }
        // 服务器结果都没有返回(可能服务器错误可能客户端断网)，断网处理:可以跳转到断网页面
        if (!window.navigator.onLine)
          router.replace('/500')
        // 根据服务器响应的错误状态码，做不同的处理
        dealError(config, message, response?.status)
        return Promise.reject(error)
      },
    )
  }

  /**
   * @description 常用请求方法封装
   */
  get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.get(url, { params, ..._object })
  }

  post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
    return this.service.post(url, params, _object)
  }

  put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.put(url, params, _object)
  }

  del<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
    return this.service.delete(url, { params, ..._object })
  }

  download(url: string, params?: object, _object = {}): Promise<BlobPart> {
    return this.service.post(url, params, { ..._object, responseType: 'blob' })
  }
}

export default new RequestHttp(config)
